package cdu.sl.aichatdemo.service.impl;

import cdu.sl.aichatdemo.constants.CacheConstants;
import cdu.sl.aichatdemo.constants.MailConstants;
import cdu.sl.aichatdemo.mapper.UserMapper;
import cdu.sl.aichatdemo.mapstruct.UserMap;
import cdu.sl.aichatdemo.pojo.dto.user.UserLoginDTO;
import cdu.sl.aichatdemo.pojo.dto.user.UserRegisterDTO;
import cdu.sl.aichatdemo.pojo.dto.user.UserUpdateInfoDTO;
import cdu.sl.aichatdemo.pojo.entity.User;
import cdu.sl.aichatdemo.pojo.entity.token.EmailCodeAuthenticationToken;
import cdu.sl.aichatdemo.pojo.vo.UserLoginVO;
import cdu.sl.aichatdemo.pojo.vo.UserProfileVO;
import cdu.sl.aichatdemo.service.EmailService;
import cdu.sl.aichatdemo.service.UserService;
import cdu.sl.aichatdemo.utils.CacheUtil;
import cdu.sl.aichatdemo.utils.JwtUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final EmailService emailService;

    private final CacheUtil cacheUtil;

    private final PasswordEncoder passwordEncoder;

    private final UserMap userMap;

    private final AuthenticationManager authenticationManager;

    private final JwtUtil jwtUtil;

    private final ObjectMapper objectMapper;

    @Override
    public void sendRegisterVerificationCode(String email) {
        // 调用邮件服务发送验证码
        String verificationCode = emailService.sendVerificationCode(email, MailConstants.REGISTER_SUBJECT, MailConstants.USER_REGISTER_TEXT);
        // 将验证码保存到缓存
        String key = CacheConstants.USER_CACHE + CacheConstants.REGISTER_VERIFICATION_CODE + email;
        cacheUtil.saveToCacheWithExpireTime(key, verificationCode, CacheConstants.REGISTER_VERIFICATION_CODE_EXPIRES_IN, TimeUnit.SECONDS);
        log.info("注册验证码{}发送已成功发送到目标邮箱：{}", verificationCode, email);
    }


    @Override
    public void sendLoginVerificationCode(String email) {
        // 调用邮件服务发送验证码
        String verificationCode = emailService.sendVerificationCode(email, MailConstants.LOGIN_SUBJECT, MailConstants.USER_LOGIN_TEXT);
        // 将验证码保存到缓存
        String key = CacheConstants.USER_CACHE + CacheConstants.LOGIN_VERIFICATION_CODE + email;
        cacheUtil.saveToCacheWithExpireTime(key, verificationCode, CacheConstants.LOGIN_VERIFICATION_CODE_EXPIRES_IN, TimeUnit.SECONDS);
        log.info("登录请求验证码{}发送已成功发送到目标邮箱：{}", verificationCode, email);
    }


    @Override
    @Transactional
    public void register(UserRegisterDTO userRegisterDTO) {
        // 从缓存获取验证码
        String key = CacheConstants.USER_CACHE + CacheConstants.REGISTER_VERIFICATION_CODE + userRegisterDTO.getEmail();
        String verificationCode = cacheUtil.getFormCache(key);
        if (verificationCode == null || verificationCode.isEmpty()) {
            log.info("缓存key{}为空,请先获取验证码!", key);
            return;
        }
        if (!verificationCode.equals(userRegisterDTO.getVerificationCode())) {
            log.info("两次密码校验不通过");
            return;
        }
        // 用完验证码之后 从缓存中删除它
        cacheUtil.cleanFromCache(key);
        User user = new User();
        String encodePwd = passwordEncoder.encode(userRegisterDTO.getPassword());
        user.setEmail(userRegisterDTO.getEmail());
        user.setPassword(encodePwd);
        save(user);
    }


    /**
     * 用户登录
     *
     * @param userLoginDTO 登录dto数据
     * @return
     */
    @Override
    public UserLoginVO login(UserLoginDTO userLoginDTO) {
        // 先完成认证
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userLoginDTO.getEmail(), userLoginDTO.getPassword());
        Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        Assert.notNull(authentication, "认证不通过");

        // 认证通过之后  , 填充VO信息并返回
        User userDetails = (User) authentication.getPrincipal();
        return buildVO(userDetails, userLoginDTO.getIsRememberMe());
    }


    @Override
    public UserProfileVO getDetails(Long userId) {
        // 先从缓存中获取
        String key = CacheConstants.USER_CACHE + CacheConstants.PROFILE_CACHE + userId;
        String userProfileJson = cacheUtil.getFormCache(key);
        if (userProfileJson == null || userProfileJson.isEmpty()) {
            // 如果没有 先从数据库获取详情 再构建缓存
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getId, userId);
            User user = this.getOne(queryWrapper);

            UserProfileVO userProfile = userMap.toProfileVO(user);
            log.info("构建缓存：{}", userProfile);

            try {
                cacheUtil.saveToCacheWithExpireTime(key, objectMapper.writeValueAsString(userProfile), CacheConstants.PROFILE_CACHE_EXPIRES_IN, TimeUnit.HOURS);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            return userProfile;
        }
        // 如果有 直接返回结果
        try {
            log.info("获取到缓存数据：{}", userProfileJson);
            return objectMapper.readValue(userProfileJson, UserProfileVO.class);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }

    }


    @Override
    public String exchangeToken(String refreshToken) {
        if (jwtUtil.validateToken(refreshToken)) {
            // 校验令牌通过
            // 查询用户信息 构建新的令牌返回
            Long userId = jwtUtil.getUserIdFromToken(refreshToken);
            LambdaQueryWrapper<User> eq = new LambdaQueryWrapper<User>().eq(User::getId, userId);
            User user = this.getOne(eq);
            return jwtUtil.generateTokenByUser(user);
        }
        return null;
    }


    @Override
    public UserProfileVO update(UserUpdateInfoDTO userUpdateInfoDTO, Long userId) {
        // 根据用户id来进行查询
        User entity = userMap.toEntityUserUpdateInfoDTO(userUpdateInfoDTO);
        this.updateById(entity);
        // 更新完毕之后 , 返回更新的实体
        return userMap.toProfileVO(entity);
    }


    @Override
    public UserLoginVO verificationCodeLogin(UserLoginDTO userLoginDTO) {
        // 以邮箱为键名 从redis中获取到验证码
        EmailCodeAuthenticationToken token = new EmailCodeAuthenticationToken(userLoginDTO.getEmail(), userLoginDTO.getVerificationCode());
        Authentication authentication = authenticationManager.authenticate(token);
        User user = (User) authentication.getPrincipal();
        return buildVO(user, userLoginDTO.getIsRememberMe());
    }


    public UserLoginVO buildVO(User user, boolean isRememberMe) {
        // 构造token
        String token = jwtUtil.generateTokenByUser(user);
        log.info("获取到token:{}", token);

        UserLoginVO vo = userMap.toLoginVO(user);
        // 如果用户选择 rememberMe , 再下发一个刷新令牌
        if (isRememberMe) {
            String refreshToken = jwtUtil.generateRefreshToken(user);
            vo.setRefreshToken(refreshToken);
        }
        vo.setToken(token);
        log.info("构造vo并返回");
        return vo;
    }


}
